
#include "Angel.h"

#include "textfile.h"


void printShaderInfoLog(GLuint obj);
void printProgramInfoLog(GLuint obj);
static char* readShaderSource(const char* shaderFile);
GLuint InitShader(const char* vShaderFile, const char* fShaderFile);

//Got this from http://www.lighthouse3d.com/opengl/glsl/index.php?oglinfo
// it prints out shader info (debugging!)
void printShaderInfoLog(GLuint obj)
{
	int infologLength = 0;
	int charsWritten  = 0;
	char *infoLog;
	glGetShaderiv(obj, GL_INFO_LOG_LENGTH,&infologLength);
	if (infologLength > 0)
	{
		infoLog = (char *)malloc(infologLength);
		glGetShaderInfoLog(obj, infologLength, &charsWritten, infoLog);
		printf("printShaderInfoLog: %s\n",infoLog);
		free(infoLog);
	}else{
		printf("Shader Info Log: OK\n");
	}
}

//Got this from http://www.lighthouse3d.com/opengl/glsl/index.php?oglinfo
// it prints out shader info (debugging!)
void printProgramInfoLog(GLuint obj)
{
	int infologLength = 0;
	int charsWritten  = 0;
	char *infoLog;
	glGetProgramiv(obj, GL_INFO_LOG_LENGTH,&infologLength);
	if (infologLength > 0)
	{
		infoLog = (char *)malloc(infologLength);
		glGetProgramInfoLog(obj, infologLength, &charsWritten, infoLog);
		printf("printProgramInfoLog: %s\n",infoLog);
		free(infoLog);
	}else{
		printf("Program Info Log: OK\n");
	}
}

namespace Angel {

	// Create a NULL-terminated string by reading the provided file
	static char*
		c_readShaderSource(const char* shaderFile)
		//readShaderSource(const char* shaderFile)
	{
		FILE* fp = fopen(shaderFile, "r");

		if ( fp == NULL ) { return NULL; }

		fseek(fp, 0, SEEK_END);
		long size = ftell(fp);

		fseek(fp, 0L, SEEK_SET);
		char* buf = new char[(GLuint)size + 1];
		fread(buf, 1, (GLuint)size, fp);

		buf[(GLuint)size] = '\0';
		printf("buf: %s\n", buf);
		fclose(fp);

		return buf;
	}

	static char*
		readShaderSource(const char* fname)
		//cpp_readShaderSource(const char* fname)
	{
		std::fstream ifile;
		ifile.open(fname,std::ios::in);
		std::string filetext;

		if(ifile.fail())
		{
			exit(0);
		}
		char * memblock;

		//as a whole
		/*if(ifile.is_open()){
		ifile.seekg(0,std::ios::end);
		int s = (int)ifile.tellg();
		GLuint size = (GLuint)s;

		memblock = new char[size+1];
		ifile.seekg(0,std::ios::beg);
		ifile.read(memblock,size);
		memblock[size] = '\0';
		printf("buf: %s\n", memblock);
		ifile.close();*/

		//by lines
		while( ifile.good() ) {
			std::string line;
			std::getline(ifile, line);
			filetext.append(line+'\n');
		}
		//std::copy(filetext.begin(),filetext.end(),memblock);
		memblock = new char[filetext.size()+1];
		strcpy(memblock,filetext.c_str());
		memblock[filetext.size()] = '\0';

		return memblock;
	}



	// Create a GLSL program object from vertex and fragment shader files
	GLuint
		InitShader(const char* vShaderFile, const char* fShaderFile)
	{
		struct Shader {
			const char*  filename;
			GLenum       type;
			GLchar*      source;
		}  shaders[2] = {
			{ vShaderFile, GL_VERTEX_SHADER, NULL },
			{ fShaderFile, GL_FRAGMENT_SHADER, NULL }
		};

		GLuint program = glCreateProgram();

		for ( int i = 0; i < 2; ++i ) {
			Shader& s = shaders[i];
			s.source = readShaderSource( s.filename );
			if ( shaders[i].source == NULL ) {
				std::cerr << "Failed to read " << s.filename << std::endl;
				exit( EXIT_FAILURE );
			}



			GLuint shader = glCreateShader( s.type );
			glShaderSource( shader, 1, (const GLchar**) &s.source, NULL );
			glCompileShader( shader );

			printShaderInfoLog(shader);

			GLint  compiled;
			glGetShaderiv( shader, GL_COMPILE_STATUS, &compiled );
			if ( !compiled ) {
				std::cerr << s.filename << " failed to compile:" << std::endl;
				GLint  logSize;
				glGetShaderiv( shader, GL_INFO_LOG_LENGTH, &logSize );
				char* logMsg = new char[logSize];
				glGetShaderInfoLog( shader, logSize, NULL, logMsg );
				std::cerr << logMsg << std::endl;
				delete [] logMsg;

				exit( EXIT_FAILURE );
			}

			delete [] s.source;

			glAttachShader( program, shader );
		}

		/* link  and error check */
		glLinkProgram(program);

		GLint  linked;
		glGetProgramiv( program, GL_LINK_STATUS, &linked );
		if ( !linked ) {
			std::cerr << "Shader program failed to link" << std::endl;
			GLint  logSize;
			glGetProgramiv( program, GL_INFO_LOG_LENGTH, &logSize);
			char* logMsg = new char[logSize];
			glGetProgramInfoLog( program, logSize, NULL, logMsg );
			std::cerr << logMsg << std::endl;
			delete [] logMsg;

			exit( EXIT_FAILURE );
		}

		/* use program object */
		glUseProgram(program);

		printProgramInfoLog(program);

		return program;
	}

}  // Close namespace Angel block
